import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import models.Config;
import models.DropboxState;
import models.Print;
import play.Logger;
import play.Play;
import play.db.jpa.Transactional;
import play.jobs.Every;
import play.jobs.Job;
import play.jobs.OnApplicationStart;
import ru.shop.dropbox.Dropbox;

import com.dropbox.client2.DropboxAPI.DeltaEntry;
import com.dropbox.client2.DropboxAPI.DeltaPage;
import com.dropbox.client2.DropboxAPI.DropboxLink;
import com.dropbox.client2.DropboxAPI.Entry;
import com.dropbox.client2.DropboxAPI.ThumbFormat;
import com.dropbox.client2.DropboxAPI.ThumbSize;
import com.dropbox.client2.RESTUtility;
import com.dropbox.client2.exception.DropboxException;

@OnApplicationStart
@Every(value = "180mn")
public class DropboxSync extends Job {
	private String thumbnailPath = Play.configuration.getProperty("shop.dropbox.thumbnail.path", "_th");
	private DropboxState state;
	private Config config;

	@Override
	public void doJob() throws Exception {
		Logger.info("Dropbox job inprogress...");
		state = DropboxState.findById(new Long(1));
		config = Config.findById(new Long(1));

		for (int i = 0; i < 5; i++) {
			if (doDelta().hasMore != true)
				break;
		}
		Logger.info("Finished (Dropbox job inprogress...)");
	}

	@Transactional
	private DeltaPage<Entry> doDelta() throws DropboxException, Exception {
		DeltaPage<Entry> delta = Dropbox.client.delta(state.cursor);
		for (DeltaEntry<Entry> entry : delta.entries) {
			Entry entryMeta = entry.metadata;
			if (isNewFile(entryMeta)) {
				DropboxLink sourceLink = getLink(entryMeta.path, true);
				String fileName = entryMeta.fileName();

				// retrieves thumbnail urls
				Map<ThumbSize, DropboxLink> thUrls = new HashMap<ThumbSize, DropboxLink>(9);
				for (ThumbSize thumbSize : ThumbSize.values()) {
					thUrls.put(thumbSize, getLink(thumbSize, fileName));
				}

				Date clientMtime = RESTUtility.parseDate(entryMeta.clientMtime);
				Date modified = RESTUtility.parseDate(entryMeta.modified);

				Print existingPrint = Print.find("byLcPath", entry.lcPath).first();
				if (existingPrint == null) {// create
					Print print = new Print(fileName, entry.lcPath, clientMtime, modified, sourceLink.url,
							thUrls.get(ThumbSize.ICON_64x64).url, thUrls.get(ThumbSize.ICON_128x128).url, thUrls.get(ThumbSize.ICON_256x256).url,
							sourceLink.expires, new ArrayList(config.defaultPrintGroups));
					if (!print.create()) {
						throw new Exception("Creation failed of Print entity: " + print);
					}
				} else {// update
					existingPrint.clientMtime = clientMtime;
					existingPrint.modifiedFile = modified;
					existingPrint.sourceUrl = sourceLink.url;
					existingPrint.linkExpires = sourceLink.expires;
					existingPrint.thumbnailS = thUrls.get(ThumbSize.ICON_64x64).url;
					existingPrint.thumbnailM = thUrls.get(ThumbSize.ICON_128x128).url;
					existingPrint.thumbnailL = thUrls.get(ThumbSize.ICON_256x256).url;
					existingPrint.groups = new ArrayList(config.defaultPrintGroups);
					existingPrint.deleted = false;
					existingPrint.save();
				}
			} else if (isDeleted(entryMeta)) {// delete
				Print print = Print.find("byLcPath", entry.lcPath).first();
				if (print != null) {
					print.deleted = true;
					print.updated = new Date();
					print.save();
					Logger.info("Print entity deleted is true: " + print);
				}
			} else {
				Logger.info("Unknown file/folder: " + entry.lcPath);
			}
		}

		state.cursor = delta.cursor;
		state.updated = new Date();
		state.save();
		return delta;
	}

	private String genThumbPath(ThumbSize thSize, String fileName) {
		fileName = fileName.substring(0, fileName.lastIndexOf("."));
		return thumbnailPath + "/" + thSize.toAPISize() + "/" + fileName + "." + ThumbFormat.PNG.toString().toLowerCase();
	}

	private boolean isDeleted(Entry entryMeta) {
		return entryMeta == null || entryMeta.isDeleted;
	}

	private boolean isNewFile(Entry entryMeta) {
		return entryMeta != null && !entryMeta.isDir && !entryMeta.isDeleted && !entryMeta.path.startsWith(thumbnailPath);
	}

	private DropboxLink getLink(String path, boolean ssl) throws DropboxException {
		return Dropbox.client.media(path, ssl);
	}

	private DropboxLink getLink(ThumbSize thSize, String fileName) throws DropboxException {
		return getLink(genThumbPath(thSize, fileName), false);
	}
}
